package com.ljcalc.business.components.calcs;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import com.ljcalc.business.components.Combination;
import com.ljcalc.business.components.CombinationsSet;
import com.ljcalc.business.components.NumberPercentage;
import com.ljcalc.business.components.RandomPool;
import com.ljcalc.business.components.RandomPoolManager;

import junit.framework.TestCase;

public class CombCalcTest extends TestCase {

	CombCalc unitUnderTest = null;
	
	private RandomPoolManager rpmmock = null;
	



	private long lastseed = 10;
	
	public void setUp() throws Exception{
		super.setUp();
		unitUnderTest = createMock(CombCalc.class, new Method[]{CombCalc.class.getMethod("initialize", null)});
		
		
		Field field = getField(unitUnderTest.getClass(), "numberPercentageList");
		field.setAccessible(true);
		field.set(unitUnderTest, initNumerPercentageList());
		

		
		//mock the random pool manager singleton
		rpmmock = createMock(RandomPoolManager.class);
		
		Field field3 = getField(RandomPoolManager.class, "instance");
		field3.setAccessible(true);
		field3.set(rpmmock, rpmmock);	
		
	}
	
	public void tearDown() throws Exception{
		super.tearDown();
	}
	
	public void testFillUpAfterPrefilling() throws Exception{
		
		Field field = getField(unitUnderTest.getClass(), "numberPercentageList");
		field.setAccessible(true);
		field.set(unitUnderTest, initNumerPercentageList());
		
		Field field2 = getField(unitUnderTest.getClass(), "COMBINATION_SET_SIZE");
		field2.setAccessible(true);
		field2.set(unitUnderTest, 10);
		
		
		
		LinkedList<NumberPercentage> npList = initNumerPercentageList();
		
		Collections.sort(npList, new Comparator<NumberPercentage>() {

			public int compare(NumberPercentage o1, NumberPercentage o2) {
				return (int) (o2.getPercentage()-o1.getPercentage());
			}
		});
		
		
		LinkedList<Long> numberList = new LinkedList<Long>();
		
		Iterator<NumberPercentage> itr1 = npList.iterator();
		while (itr1.hasNext()) {
			NumberPercentage numberPercentage = (NumberPercentage) itr1.next();
			numberList.add(new Long(numberPercentage.getNum()));
		}
		
		RandomPool p1 = new RandomPool(keepListFresh(10));
		RandomPool p2 = new RandomPool(keepListFresh(35));
		
		
		Iterator<Long> itrnl = numberList.iterator();
		while (itrnl.hasNext()) {
			Long object = (Long) itrnl.next();
			while(p2.remove(object));
		}
		
		expect(rpmmock.getRandomPool(1, 10)).andReturn(p1);
		expect(rpmmock.getRandomPool(1, 35, numberList)).andReturn(p2);
//		expect(rpmmock.getRandomPool(1, 10)).andReturn(new RandomPool(keepListFresh(10)));
//		expect(rpmmock.getRandomPool(1, 10)).andReturn(new RandomPool(keepListFresh(10)));
//		expect(rpmmock.getRandomPool(1, 10)).andReturn(new RandomPool(keepListFresh(10)));
		CombinationsSet result = null;
		
		replay(rpmmock);
		
		result = unitUnderTest.prefilling();
		result = unitUnderTest.fillUp(result);
		
		int numberOf1 = 0;
		int numberOf2 = 0;
		int numberOf3 = 0;
		int numberOf5 = 0;
		
		Iterator<Combination> itr = result.iterator();
		while (itr.hasNext()) {
			Combination combination = (Combination) itr.next();
			
			if(combination.has(1)){
				numberOf1++;
			}
			if(combination.has(2)){
				numberOf2++;
			}
			if(combination.has(3)){
				numberOf3++;
			}
			if(combination.has(5)){
				numberOf5++;
			}


		}
		
		assertEquals(2, numberOf1);
		assertEquals(3, numberOf2);
		assertEquals(4, numberOf3);
		assertEquals(3, numberOf5);
		
		System.out.println(result);
		
		System.out.println(result.organize());
		
		verify(rpmmock);
		
	}
	
	/**
	 * Combset with 10 combinations to be filled
	 * numbers are from 1 to 35
	 * @throws Exception
	 */
	public void testFillUp() throws Exception{
		
		Field field = getField(unitUnderTest.getClass(), "numberPercentageList");
		field.setAccessible(true);
		field.set(unitUnderTest, new LinkedList());
		
		expect(rpmmock.getRandomPool(1, 35, new LinkedList())).andReturn(new RandomPool(keepListFresh(35)));
		
		replay(rpmmock);
		
		CombinationsSet set = unitUnderTest.fillUp(new CombinationsSet(10));
		
		System.out.println(set);

		
		verify(rpmmock);
	}
	
	
	public void testPrefilling1000() throws Exception{
		
		Field field = getField(unitUnderTest.getClass(), "numberPercentageList");
		field.setAccessible(true);
		field.set(unitUnderTest, initNumerPercentageList());
		
		Field field2 = getField(unitUnderTest.getClass(), "COMBINATION_SET_SIZE");
		field2.setAccessible(true);
		field2.set(unitUnderTest, 1000);
		
		RandomPool pool = new RandomPool(keepListFresh(1000));
		expect(rpmmock.getRandomPool(1, 1000)).andReturn(pool);
		replay(rpmmock);
		replay(unitUnderTest);
		
		CombinationsSet result = unitUnderTest.prefilling();
		
		int numberOf1 = 0;
		int numberOf2 = 0;
		int numberOf3 = 0;
		int numberOf5 = 0;
		
		Iterator<Combination> itr = result.iterator();
		while (itr.hasNext()) {
			Combination combination = (Combination) itr.next();
			
			if(combination.has(1)){
				numberOf1++;
			}
			if(combination.has(2)){
				numberOf2++;
			}
			if(combination.has(3)){
				numberOf3++;
			}
			if(combination.has(5)){
				numberOf5++;
			}


		}
		
		assertEquals(200, numberOf1);
		assertEquals(300, numberOf2);
		assertEquals(400, numberOf3);
		assertEquals(330, numberOf5);
		
		System.out.println(pool.size());
		
		verify(rpmmock);
		verify(unitUnderTest);
		
		
		
	}
	
	public void testPrefilling10() throws Exception{
		
		Field field = getField(unitUnderTest.getClass(), "numberPercentageList");
		field.setAccessible(true);
		field.set(unitUnderTest, initNumerPercentageList());
		
		Field field2 = getField(unitUnderTest.getClass(), "COMBINATION_SET_SIZE");
		field2.setAccessible(true);
		field2.set(unitUnderTest, 10);
				
		expect(rpmmock.getRandomPool(1, 10)).andReturn(new RandomPool(keepListFresh(10)));
//		expect(rpmmock.getRandomPool(1, 10)).andReturn(new RandomPool(keepListFresh(10)));
//		expect(rpmmock.getRandomPool(1, 10)).andReturn(new RandomPool(keepListFresh(10)));
//		expect(rpmmock.getRandomPool(1, 10)).andReturn(new RandomPool(keepListFresh(10)));
		
		replay(rpmmock);
		replay(unitUnderTest);
		
		CombinationsSet result = unitUnderTest.prefilling();
		
		int numberOf1 = 0;
		int numberOf2 = 0;
		int numberOf3 = 0;
		int numberOf5 = 0;
		
		Iterator<Combination> itr = result.iterator();
		while (itr.hasNext()) {
			Combination combination = (Combination) itr.next();
			
			if(combination.has(1)){
				numberOf1++;
			}
			if(combination.has(2)){
				numberOf2++;
			}
			if(combination.has(3)){
				numberOf3++;
			}
			if(combination.has(5)){
				numberOf5++;
			}


		}
		
		assertEquals(2, numberOf1);
		assertEquals(3, numberOf2);
		assertEquals(4, numberOf3);
		assertEquals(3, numberOf5);
		
		System.out.println(result);
		
		verify(rpmmock);
		verify(unitUnderTest);
		
		
		
	}
	
	private List<Long> keepListFresh(int n) {
		Random r = new Random(lastseed);
		LinkedList<Long> list = null;
		long rvalue = 0;
		
		if(list==null)
			list = new LinkedList<Long>();
		
		if(list.size()==0){
			int upperLimit = 10*n;
			for(int i = 0;i<upperLimit;i++){
				rvalue = r.nextLong()%n;
				if(rvalue<0){
					rvalue = Math.abs(rvalue);
				}
				list.add(new Long(rvalue + 1));
			}
		}
		lastseed++;
		return list;
	}

	private LinkedList<NumberPercentage> initNumerPercentageList(){
		LinkedList<NumberPercentage> list = new LinkedList<NumberPercentage>();
		
		list.add(new NumberPercentage(5,33));
		list.add(new NumberPercentage(1,20));
		list.add(new NumberPercentage(2,30));
		list.add(new NumberPercentage(3,40));

		
		return list;
		
	}
	
	
	private Field getField(Class clazz, String fieldName) throws NoSuchFieldException {
		try {
			return clazz.getDeclaredField(fieldName);
		} catch (NoSuchFieldException ex) {
			if (!clazz.getSuperclass().equals(Object.class)) {
				return getField(clazz.getSuperclass(), fieldName);
			} else {
				throw ex;
			}
		}
	}

}
